import React from 'react';

// 像这种 因为不确定范型是否拥有length属性 所以就会报错
// function echoArr<T>(arg: T): T {
//   console.log(arg.length)
//   return arg
// }

// 但是如果我们定义一个接口
interface IWithLength {
  length: number;
}

// 然后使用extends的方式来约束范型的属性  就可以了
function echoLength<T extends IWithLength>(arg: T): T {
  console.log(arg.length)
  return arg
}


echoLength('string')
echoLength({ length: 123 })
echoLength([1231, 12, 31, 23,])


// 范型还可以使用在class类中
class Queue {
  private data: any[] = [];

  push(value: any) {
    return this.data.push(value)
  };

  pop() {
    return this.data.pop()
  }
}

// 当上面的Queue没有使用范型时 因为压入的类型它不知道 所以就无法正常的报错  我们这里pop出一个字符串调用toFixed方法依然没有报错
const queue = new Queue()
console.log(queue.push('123').toFixed())
console.log(queue.pop().toFixed())

// 如果使用范型的话 约束它的类型
class NewQueue<T> {
  private data: T[] = []

  push(value: T) {
    return this.data.push(value)
  }

  pop() {
    return this.data.pop()
  }
}

const newQueue = new NewQueue<number>()
// 这里就可以正常报错
// console.log(newQueue.push('123').toFixed())
console.log(newQueue.push(123).toFixed())
console.log(newQueue.pop()?.toFixed())

// 还可以在接口中使用
interface KeyPair<T, U> {
  key: T;
  pair: U;
}

let a: KeyPair<number, string> = { key: 123, pair: '123' }

// 注意 如果要通过接口来规定函数的形状 千万不能加键名 如果加了键名就只能表示是一个对象了 而且只能有这一个属性 而且需要使用变量赋值的方式定义函数 不能使用function abc(){}
// 这样很容易报错 建议不要使用接口专门定义一个函数的形状  接口一般都用于定义对象的形状
interface Func<T> {
  (a: T, b: T): T;
}

// 在接口中定义函数的写法只有一下两种
interface Function {
  a(): void;

  b: () => void;
}

const aFunc: Func<number> = function (x, y) {
  return x + y
}

const aFunc2: Func<number> = (x, y) => {
  return x + y
}

// 一般使用type来定义函数的形状  建议使用type定义函数的形状
type a<T> = (x: T, y: T) => T

const abc: a<number> = (x, y) => {
  return y
}
